<?php

namespace app\admin\controller;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\App;
use app\BaseController;
use GeoIp2\Database\Reader;
use Detection\MobileDetect;
use app\common\model\auth\AuthMenuModel;
use app\common\model\auth\AuthRoleModel;
use app\common\exception\RenderException;
use app\common\model\auth\AuthAdminModel;
use app\common\model\auth\AuthAdminOperateLogModel;

class Base extends BaseController
{
    //挂在一个全局管理账号信息
    protected $adminInfo = null;

    public function __construct(App $app)
    {
        parent::__construct($app);

        $this->checkLogin();
        $this->checkAuthMenu();
        $this->operateLog();
    }

    /**
     * 校验登录
     * @throws RenderException
     */
    private function checkLogin()
    {
        $decoded = JWT::decode(request()->header('token'), new Key(config('jwt.key'), config('jwt.alg')));
        if ($decoded->exp < time()) {
            throw new RenderException('登录已过期，请重新登录');
        }
        $this->adminInfo = (new AuthAdminModel)->where('id', $decoded->admin_id)->find();
        if (!$this->adminInfo) {
            throw new RenderException('登录账号信息不存在或已删除');
        }

        if ($this->adminInfo['status'] == AuthAdminModel::STATUS_FREEZE) {
            throw new RenderException('账号已被冻结，请联系管理员');
        }

        /** @see get_admin_info() */
        write_header([
            'admin-info' => $this->adminInfo
        ]);
    }

    /**
     * 校验菜单权限
     * @throws RenderException
     */
    private function checkAuthMenu()
    {
        if (empty($this->adminInfo['role_ids'])) {
            throw new RenderException('账户未配置角色，请联系管理员');
        }

        if (!(new AuthAdminModel)->isAdmin($this->adminInfo)) {
            $menuUrlData = (new AuthMenuModel)->field('url')->column('url');
            $url = strtolower(ltrim(request()->baseUrl(), '/'));
            if (in_array($url, array_map('strtolower', $menuUrlData))) {
                $ruleMenuData = (new AuthRoleModel)->whereIn('id', explode(',', $this->adminInfo['role_ids']))
                    ->where('status', AuthRoleModel::STATUS_OPEN)
                    ->field('menu_ids')
                    ->select()->toArray();
                $ruleMenuIds = [];
                foreach ($ruleMenuData as $v) {
                    $ruleMenuIds = array_merge($ruleMenuIds, explode(',', $v['menu_ids']));
                }
                $menuUrlData = (new AuthMenuModel)->whereIn('id', array_unique($ruleMenuIds))
                    ->field('url')
                    ->select()->toArray();
                if (!in_array($url, array_map('strtolower', array_column($menuUrlData, 'url')))) {
                    throw new RenderException('您未拥有菜单权限，请联系管理员');
                }
            }
        }
    }

    /**
     * 记录操作日志
     */
    public function operateLog()
    {
        try {
            $ispDbReader = new Reader('./static/GeoLite2-City_20240611/GeoLite2-City.mmdb');
            $record = $ispDbReader->city(request()->ip());
            $address = '';
            if (isset($record->country->names['zh-CN'])) {
                $address .= $record->country->names['zh-CN'];
            }
            if (isset($record->mostSpecificSubdivision->names['zh-CN'])) {
                $address .= ' ' . $record->mostSpecificSubdivision->names['zh-CN'];
            }
            if (isset($record->city->names['zh-CN'])) {
                $address .= ' ' . $record->city->names['zh-CN'];
            }

            $latitude = $record->location->latitude;
            $longitude = $record->location->longitude;
        } catch (\Exception $e) {
        }

        try {
            $mobileDetect = new MobileDetect;
            if ($mobileDetect->isMobile()) {
                $device = '手机';
            } elseif ($mobileDetect->isTablet()) {
                $device = '平板';
            } else {
                $device = '电脑';
            }
        } catch (\Exception $e) {

        }

        $url = strtolower(ltrim(request()->baseUrl(), '/'));
        if (preg_match('/\b(add|edit|change|del|status)\b/i', $url)) {
            $menuInfo = (new AuthMenuModel)->where('url', $url)->find();

            $param = request()->param();
            unset($param['password'], $param['confirm_password']);

            $operateLogId = (new AuthAdminOperateLogModel)->insertGetId([
                'admin_id' => $this->adminInfo['id'],
                'url' => $url,
                'menu_id' => $menuInfo['id'] ?? 0,
                'menu_name' => $menuInfo['name'] ?? 0,
                'menu_name_path' => (new AuthMenuModel)->getMenuNamePath($menuInfo['id'] ?? 0),
                'method' => request()->method(),
                'param' => json_encode($param, JSON_UNESCAPED_UNICODE),
                'ip' => request()->ip(),
                'address' => $address ?? '',
                'latitude' => $latitude ?? '',
                'longitude' => $longitude ?? '',
                'user_agent' => $_SERVER['HTTP_USER_AGENT'],
                'device' => $device,
                'create_time' => date('Y-m-d H:i:s'),
            ]);

            write_header([
                'operate-log-id' => $operateLogId
            ]);
        }
    }

    function __destruct()
    {
        $operateLogId = get_operate_log_id();
        if ($operateLogId) {
            $requestResponseInfo = get_request_response_info();
            (new AuthAdminOperateLogModel)->where('id', $operateLogId)->update([
                'end_time' => date('Y-m-d H:i:s'),
                'response_result' => (isset($requestResponseInfo['code']) && $requestResponseInfo['code'] === 0) ? AuthAdminOperateLogModel::RESPONSE_RESULT_SUCCESS : AuthAdminOperateLogModel::RESPONSE_RESULT_ERROR,
                'response_describe' => $requestResponseInfo['msg'] ?? ''
            ]);
        }
    }

}